(* L #5:DBUNIT.LST.TEXT*) {make sure you leave plenty of room for the listing}
(*$S+*)
UNIT DBUNIT; (*version 1.2 - 5 Feb, 1980*)
  (*Copyright 1980 Kenneth L. Bowles. All rights reserved. Permission
    is hereby granted to use this material for any non-commercial 
    purpose*)
INTERFACE
CONST
  LASTWRKINDEX=20;
  LONGINTSIZE=14;
  SETSIZE=47;
  NAMESTRSIZE=30;
  LASTFILENUM=4;
  
TYPE
  BYTE=0..255;
  DBWRKINDEX=0..LASTWRKINDEX;
  DBERRTYPE=0..100; (*not a scalar to conserve symbols*)
  DBFILENUM=0..LASTFILENUM;
  DBFIELDTYPES=(GROUPF, STRINGF, BYTEF, INTEGERF, LONGINTF,
                ADDRCOUPLEF, SETF, PICF, TEXTF);
                
  DBLEVELTYPE=(NONET, GROUPT, RECORDT, FIELDT);
  DBFINDRULE=(ASCENDING, DESCENDING, RANDOM);
  
  FILETYPE=FILE; (*compiler won't acccept 'file' as parameter type*)
  
  FLDDESCRIPTOR=
    PACKED RECORD
      OVERLINK:BYTE;
      SWITCHES:BYTE; (*bit 0 = tagged; bit 1 = fixedwidth *)
      MAXWIDTH:INTEGER;
      USECOUNT:BYTE;
      FLDTYPE:DBFIELDTYPES;
      FLDREF:INTEGER; (*points to descriptor of FLDTYPE; =0 IF NOT GROUPF*)
      (*following may get moved to Layout later*)
      ROW:BYTE;
      DATACOL:BYTE;
      LABELCOL:BYTE;
      CONTROLBITS:BYTE;
      NAME:STRING[1]  (*generally will be expanded out of rangechecking*)
    END;
  FLDDESPTR=^FLDDESCRIPTOR;
  

VAR
  DBTYPECHECK:BOOLEAN; (*if false can't use fixed length records*)
  DEBUGGING:BOOLEAN;
  F0,F1,F2,F3,F4:FILETYPE;
  
  DBMAIL:
    RECORD CASE DBMAILTYPE: DBFIELDTYPES OF
      GROUPF: ( ); (*TO BE DEFINED*)
      STRINGF: (STRG:STRING[255]);
      BYTEF: (BYT:BYTE);
      INTEGERF: (INT:INTEGER);
      LONGINTF: (LINT:INTEGER[LONGINTSIZE]);
      ADDRCOUPLE:(PGE:INTEGER;
                  GRP:INTEGER;
                  REC:INTEGER);
      SETF: (SETT:PACKED ARRAY[0..SETSIZE] OF BOOLEAN);
      PICF: ( ); (* PICTURES TO BE DEFINED *)
      TEXTF: (TXT: PACKED ARRAY[0..255] OF CHAR)
    END (*DBMAIL*);
    
  DBIORESULT:INTEGER;
  DBTRACESET:SET OF DBERRTYPE;
    
(*TRAVERSAL PRIMITIVES*)
  FUNCTION DBHOME(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBHEAD(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBNEXT(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBTAIL(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBSEEK(WI:DBWRKINDEX; WHICHITEM:INTEGER):DBERRTYPE;
  FUNCTION DBDESCEND(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBASCEND(WI:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBFINDREC(WI:DBWRKINDEX; RULE:DBFINDRULE; FIELDNUM:INTEGER;
                     KEY:STRING; VAR RECNUM:INTEGER;
                     VAR FOUND:BOOLEAN):DBERRTYPE;
  
(*DATA TRANSFER PRIMITIVES*)
  FUNCTION DBCOPY(SOURCE,DESTINATION:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBEMPTYITEM(DESTINATION:DBWRKINDEX; LVL:DBLEVELTYPE;
                                               TAG:INTEGER):DBERRTYPE;
  FUNCTION DBDELETE(DESTINATION:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBBLANK(DESTINATION:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBREPLACE(SOURCE,DESTINATION:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBRESERVE(DESTINATION:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBGET(SOURCE:DBWRKINDEX):DBERRTYPE;
  FUNCTION DBPUT(DESTINATION:DBWRKINDEX):DBERRTYPE;

(*SUPPORT PRIMITIVES*)
  FUNCTION DBWRITEFIELD(VAR FID:TEXT; SOURCE:DBWRKINDEX):DBERRTYPE;
  PROCEDURE DBGETDESCRIPTOR(LEVEL:DBLEVELTYPE; 
                            DESCRIPTORNUM:INTEGER;
                            VAR PTR:FLDDESPTR);
  FUNCTION DBTAG(NAME:STRING; SOURCE:DBWRKINDEX; VAR ITEMNUM:INTEGER):DBERRTYPE;
  
(*WORKAREA PRIMITIVES*)
  FUNCTION DBWRKOPEN(WI:DBWRKINDEX; SIZE:INTEGER):DBERRTYPE;
  FUNCTION DBWRKCLOSE(WI:DBWRKINDEX):DBERRTYPE;
  PROCEDURE ZEROWORKAREA(WI:DBWRKINDEX);

(*FILE PRIMITIVES*)
  FUNCTION DBFOPEN(FNUM:DBFILENUM; TITLE:STRING):DBERRTYPE;
  FUNCTION DBFCLOSE(FNUM:DBFILENUM):DBERRTYPE;
  FUNCTION DBFCREATE(FNUM:DBFILENUM; WASCRATCH:DBWRKINDEX;
                                     SPEXTITLE,NEWTITLE:STRING):DBERRTYPE;
  FUNCTION DBFREMOVE(FNUM:DBFILENUM):DBERRTYPE;
  FUNCTION DBGETPAGE(FNUM:DBFILENUM; DESTINATION:DBWRKINDEX;
                                     PAGENUM:INTEGER):DBERRTYPE;
  FUNCTION DBPUTPAGE(FNUM:DBFILENUM; SOURCE:DBWRKINDEX;
                                     PAGENUM:INTEGER):DBERRTYPE;

(*DESCRIPTOR INITIALIZING PRIMITIVES*)
  FUNCTION DBGROUPINIT(FNUM:DBFILENUM; VAR GROUPNUM:INTEGER;
                                              GROUPNAME:STRING):DBERRTYPE;
  FUNCTION DBGROUPRELEASE(GROUPNUM:INTEGER):DBERRTYPE;
  
(*INITIALIZATION*)
  PROCEDURE DBINITIALIZE;
  
(*ORDERLY TERMINATION*)
  FUNCTION DBCLOSEDOWN:DBERRTYPE;
  
(*ERROR REPORTING AND DIAGNOSTICS*)
  PROCEDURE DBSHOWERROR(S:STRING; ERRNUM:DBERRTYPE);
  PROCEDURE DBITEMINFO(WI:DBWRKINDEX; VAR LEVEL:DBLEVELTYPE;
           VAR ITEMNUM,OFFSET,DESCRIPTORNUM:INTEGER; VAR NAME:STRING);
  
(**************************************************************)
IMPLEMENTATION
CONST
  PAGELASTBYTE=4095;
  LASTSPECIALGROUP=6;
  LASTWRKSTACKSLOT=9;
  LASTGROUPDESCRIPTOR=255;
  LASTRECDESCRIPTOR=255;
  LASTFIELDDESCRIPTOR=255;
  LINKESCAPE=240;
  DBNUL=0;
  ONEITEMRECLINK=6;
  
TYPE
  PAGEPTR=0..PAGELASTBYTE;
  PAGETYPE=PACKED ARRAY[PAGEPTR] OF BYTE;
  
  (*work area information block - WIB *)
  WIBENTRY=
    RECORD
      OFFSET:PAGEPTR;
      LEVEL:DBLEVELTYPE;
      DESCRIPTORNUM:INTEGER;
      ITEMNUM:INTEGER;
    END;
  TOSRANGE=0..LASTWRKSTACKSLOT;
  WIBTYPE=ARRAY[TOSRANGE] OF WIBENTRY;
  WIBPTR=^WIBTYPE;
  
  (*following are dummy types used for heap allocation of workareas*)
  WATYPE=PACKED ARRAY[0..63] OF BYTE; (* WA will be multiple of these*)
  WAPTR=^WATYPE;
  ONEWORDPTR=^INTEGER;
  REFLIST=ARRAY[0..0] OF INTEGER; (*index with range checking off*)
  
  (*fixed layout parts of descriptors*)
  GRPDESCRIPTOR=
    PACKED RECORD
      OVERLINK:BYTE; (*descriptor longer than 240 bytes not allowed*)
      SWITCHES:BYTE; (*packed array gets allocated in whole words*)
                     (*bit 0 = tagged; bit 1 = linked *)
      RECLINK:BYTE;
      FILLER:BYTE;
      RECNUM:REFLIST;
      (*expand here with additional recnum's*)
    END;
  GRPDESPTR=^GRPDESCRIPTOR;
  
  RECDESCRIPTOR=
    PACKED RECORD
      OVERLINK:BYTE;
      SWITCHES:BYTE; (*bit 0 = tagged; bit 1 = fixed width; bit 2 = sparse *)
      SIZE:INTEGER;
      FIRSTLITEMNUM:BYTE; (*set to 1 more than last fixed itemnumber if
                            there are only fixed fields in the record*)
      USECOUNT:BYTE;
      LAYOUT:BYTE; (*on a large system this could be declared TAG*)
      LASTFLDLINK:BYTE; (*points to name field, indirect upper bound of
                            FLDREF array*)
      FLDREF:ARRAY [0..0] OF
               PACKED RECORD
                 FDNUM: 0..LASTFIELDDESCRIPTOR;
                 FLDOFFSET:BYTE; (*for fixed size fields; =0 for linked*)
               END;
      (*expand here with additional fldref's*)
    END;
  RECDESPTR=^RECDESCRIPTOR;

  CRACKSWTYPE=  (*for accessing individual switch control bits*)
    PACKED RECORD
      CASE BOOLEAN OF
        TRUE:(BL:BYTE; BH:BYTE);
        FALSE:(A:PACKED ARRAY[0..15] OF BOOLEAN);
      END (*CRACKSWTYPE*);

VAR
  HEAPMARKER:ONEWORDPTR;
  OPENFILES: PACKED ARRAY[0..LASTFILENUM] OF BOOLEAN;
  
  (*page numbers of fixed numbered groups at beginning of file*)
  SPECIALGROUPPAGE: ARRAY[0..LASTSPECIALGROUP] OF INTEGER;
  
  (*all access to workareas flows via WRKTABLE*)
  WRKTABLE: ARRAY[DBWRKINDEX] OF
    RECORD
      TOS: TOSRANGE; (*top of stack*)
      WIB: WIBPTR; (*points to stack of offsets in WIB; NIL if none allocated*)
      WSIZE: INTEGER; (*size of Workarea in bytes*)
      SPACEINUSE: INTEGER; (*initially 0*)
      WA: WAPTR  (*the workarea itself*)
    END;
      
  (*all access to on-line descriptors is via these arrays*)
  ACTIVEGROUPS: ARRAY[0..LASTGROUPDESCRIPTOR] OF GRPDESPTR;
  ACTIVERECORDS: ARRAY[0..LASTRECDESCRIPTOR] OF RECDESPTR;
  ACTIVEFIELDS: ARRAY[0..LASTFIELDDESCRIPTOR] OF FLDDESPTR;
  
  (*Lower and Upper bound for tracing*)
  TRACELB,TRACEUB:INTEGER;
  
      
